<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/5/12 0012
 * Time: 10:16
 */

namespace App\Socket\Services;


use App\Socket\Models\Protocol;
use Predis\Client;

class SocketServer
{
    /**
     * @var SocketServer
     */
    private static $instance;
    /**
     * @var \swoole_websocket_server
     */
    private $server;

    /**
     * @return SocketServer
     */
    public static function instance()
    {
        if (self::$instance == null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * 启动server
     */
    public function start()
    {
        //清除缓存op_cache apc_cache
        $this->opCacheClear();
        $this->server = new \swoole_websocket_server(config('socket.websocket.host'), config('socket.websocket.port'));
        $this->server->set(config('socket.websocket.options'));
        //收到客户端消息
        $this->server->on('message', array(SocketServerEvent::instance(), 'onMessage'));
        //worker、task进程启动 -- 添加定时器
        $this->server->on('workerstart', array(SocketServerEvent::instance(), 'onWorkStart'));
        //开启http
        $this->server->on('request', array(SocketServerEvent::instance(), 'onRequest'));
        //task进程
        $this->server->on('task', array(SocketServerEvent::instance(), 'onTask'));
        $this->server->on('finish', array(SocketServerEvent::instance(), 'onFinish'));
        //断开连接
        $this->server->on('close', array(SocketServerEvent::instance(), 'onClose'));
        //显示启动信息
        $this->showStartInfo();
        //启动server
        $this->server->start();
    }

    /**
     * 清除缓存 op_cache apc_cache
     */
    private function opCacheClear()
    {
        if (function_exists('apc_clear_cache')) {
            apc_clear_cache();
        }
        if (function_exists('opcache_reset')) {
            opcache_reset();
        }
    }

    /**
     * 显示server启动信息
     */
    private function showStartInfo()
    {
        //打印输出控制台启动提示
        echo '-----------------swoole websocket------------------' . PHP_EOL;
        echo 'System OS:' . PHP_OS . php_uname('r') . PHP_EOL;
        echo 'User:' . get_current_user() . PHP_EOL;
        echo 'PHP Version:' . PHP_VERSION . PHP_EOL;
        echo 'Swoole Version:' . swoole_version() . PHP_EOL;
        echo 'Daemonize:' . (config('socket.websocket.options.daemonize') ? 'true' : 'false') . PHP_EOL;
        echo 'Process:' . getmypid() . PHP_EOL;
        echo 'WebSocket Host:' . config('socket.websocket.host') . PHP_EOL;
        echo 'WebSocket Port:' . config('socket.websocket.port') . PHP_EOL;
        echo '---------------------------------------------------' . PHP_EOL;
    }

    /**
     * 停止server
     */
    public function stop()
    {
        $pidFile = config('socket.websocket.options.pid_file');
        if (!file_exists($pidFile)) {
            echo "pid file : {$pidFile} not exist " . PHP_EOL;
            return;
        }
        $pid = file_get_contents(config('socket.websocket.options.pid_file'));
        if (!\swoole_process::kill($pid)) {
            echo "pid : {$pid} not exist " . PHP_EOL;
            return;
        }
        echo "websocket server stop at " . date('Y-m-d H:i:s') . PHP_EOL;
    }

    /**
     * 重启服务
     */
    public function restart()
    {
        $this->stop();
        sleep(1);
        $this->start();
    }

    /**
     * 绑定连接的用户ID
     * @param $fd
     * @param $userId
     */
    public function bind($fd, $userId)
    {
        $this->server->bind($fd, $userId);
    }

    /**
     * 获取fd连接信息
     * @param $fd
     * @return array|bool
     * {
     *  uid:1, //有绑定账号id的时候才会有
     *  websocket_status:3,
     *  server_port:9501,
     *  server_fd:6,
     *  socket_type:1,
     *  remote_port:52353,
     *  reactor_id:0,
     *  connect_time:1526439392,
     *  last_time:1526439392,
     *  close_errno:0
     * }
     */
    public function getConnectionInfo($fd)
    {
        return $this->server->connection_info($fd);
    }

    /**
     * 推送消息
     * @param $fd
     * @param Protocol $protocol
     */
    public function push($fd, Protocol $protocol)
    {
        $this->server->push($fd, $protocol->toJson());
    }

    /**
     * 关闭连接
     * @param int $fd
     */
    public function close(int $fd)
    {
        $this->server->close($fd);
    }

    /**
     * 推送通知
     * @param Protocol $protocol
     */
    public function pushAll(Protocol $protocol)
    {
        $redis = new Client(config('database.redis.default'));
        $data = $protocol->toJson();
        $redis->rpush(config('socket.websocket.channel'),$data);
    }
}